#pragma once
#ifndef __PLATFORM_TOOLCHAIN_H__
#define __PLATFORM_TOOLCHAIN_H__

// Warning for unsupported compilers
#if !defined(__GNUC__)      /* GCC        */                                                                                                                                                                                                   \
    && !defined(__CC_ARM)   /* ARMCC      */                                                                                                                                                                                                   \
    && !defined(__clang__)  /* LLVM/Clang */                                                                                                                                                                                                   \
    && !defined(__ICCARM__) /* IAR        */
#warning "This compiler is not yet supported."
#endif

/** PLATFORM_PACKED
 *  Pack a structure, preventing any padding from being added between fields.
 *
 *  @code
 *  #include "platform_toolchain.h"
 *
 *  PLATFORM_PACKED(struct) foo {
 *      char x;
 *      int y;
 *  };
 *  @endcode
 */
#ifndef PLATFORM_PACKED
#if defined(__ICCARM__)
#define PLATFORM_PACKED(struct) __packed struct
#else
#define PLATFORM_PACKED(struct) struct __attribute__((packed))
#endif
#endif

/** PLATFORM_NOINLINE
 *  Declare a function that must not be inlined.
 *
 *  @code
 *  #include "PLATFORM_toolchain.h"
 *
 *  PLATFORM_NOINLINE void foo() {
 *
 *  }
 *  @endcode
 */
#ifndef PLATFORM_NOINLINE
#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM)
#define PLATFORM_NOINLINE __attribute__((noinline))
#elif defined(__ICCARM__)
#define PLATFORM_NOINLINE _Pragma("inline=never")
#else
#define PLATFORM_NOINLINE
#endif
#endif

/** PLATFORM_FORCEINLINE
 *  Declare a function that must always be inlined. Failure to inline
 *  such a function will result in an error.
 *
 *  @code
 *  #include "PLATFORM_toolchain.h"
 *
 *  PLATFORM_FORCEINLINE void foo() {
 *
 *  }
 *  @endcode
 */
#ifndef PLATFORM_FORCEINLINE
#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM)
#define PLATFORM_FORCEINLINE static inline __attribute__((always_inline))
#elif defined(__ICCARM__)
#define PLATFORM_FORCEINLINE _Pragma("inline=forced") static
#else
#define PLATFORM_FORCEINLINE static inline
#endif
#endif
#endif //!<__PLATFORM_TOOLCHAIN_H__

/** PLATFORM_UNREACHABLE
 *  An unreachable statement. If the statement is reached,
 *  behaviour is undefined. Useful in situations where the compiler
 *  cannot deduce the unreachability of code.
 *
 *  @code
 *  #include "PLATFORM_toolchain.h"
 *
 *  void foo(int arg) {
 *      switch (arg) {
 *          case 1: return 1;
 *          case 2: return 2;
 *          ...
 *      }
 *      PLATFORM_UNREACHABLE;
 *  }
 *  @endcode
 */
#ifndef PLATFORM_UNREACHABLE
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__CC_ARM)
#define PLATFORM_UNREACHABLE __builtin_unreachable()
#else
#define PLATFORM_UNREACHABLE while (1)
#endif
#endif

/** PLATFORM_WEAK
 *  Mark a function as being weak.
 *
 *  @note
 *  Functions should only be marked as weak in the source file. The header file
 *  should contain a regular function declaration to insure the function is emitted.
 *  A function marked weak will not be emitted if an alternative non-weak
 *  implementation is defined.
 *
 *  @note
 *  Weak functions are not friendly to making code re-usable, as they can only
 *  be overridden once (and if they are multiply overridden the linker will emit
 *  no warning). You should not normally use weak symbols as part of the API to
 *  re-usable modules.
 *
 *  @code
 *  #include "PLATFORM_toolchain.h"
 *
 *  PLATFORM_WEAK void foo() {
 *      // a weak implementation of foo that can be overriden by a definition
 *      // without  __weak
 *  }
 *  @endcode
 */
#ifndef PLATFORM_WEAK
#if defined(__ICCARM__)
#define PLATFORM_WEAK __weak
#else
#define PLATFORM_WEAK __attribute__((weak))
#endif
#endif

/** PLATFORM_DEPRECATED("message string")
 *  Mark a function declaration as deprecated, if it used then a warning will be
 *  issued by the compiler possibly including the provided message. Note that not
 *  all compilers are able to display the message.
 *
 *  @code
 *  #include "PLATFORM_toolchain.h"
 *
 *  PLATFORM_DEPRECATED("don't foo any more, bar instead")
 *  void foo(int arg);
 *  @endcode
 */
#ifndef PLATFORM_DEPRECATED
#if defined(__CC_ARM)
#define PLATFORM_DEPRECATED(M) __attribute__((deprecated))
#elif defined(__GNUC__) || defined(__clang__)
#define PLATFORM_DEPRECATED(M) __attribute__((deprecated(M)))
#else
#define PLATFORM_DEPRECATED(M)
#endif
#endif
